#include <fcntl.h>
#include <sys/shm.h>
#include "../include/commonSHM.h"
#include "../include/general.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>


#include "../include/sem.h"
#include "../include/general.h"
#include "../include/filesystem.h"
#include "../include/commonSHM.h"
#include "../include/common.h"

#define MAX_MSG_SIZE 512

/* fuente tp 2008 sima y juan */

struct shmCDT{
    char * pshm;
    int sem;
    int shmid;
};


int getShmKey(shmADT s)
{
    return s->shmid;
}


char * getShm(shmADT s)
{
    return s->pshm;
}

shmADT
open_conn_shm (int id, int flag)
{
    shmADT shm;
    int auxshm, auxsem, shmflag;
    void * pshm;
    char * cpshm;

    if (id < 0)
        return NULL;

    if ((shm = malloc (sizeof (struct shmCDT))) == NULL)
        return NULL;

    if(flag)
        shmflag = IPC_CREAT | 0666;
    else
        shmflag = 0666;

    shm->shmid = id;

    /* Abro SHM y Semaforo para pedir conexion */
    if ((auxshm = shmget (id, MAX_MSG_SIZE, shmflag)) == -1)
    {
        free (shm);
        return NULL;
    }

    if ((pshm = shmat (auxshm, NULL, 0666)) == (void*) -1)
    {
        free (shm);
        return NULL;
    }

    cpshm = (char*) pshm;
     if ((auxsem = initsem(id)) == -1)
     {
         free (shm);
         return NULL;
     }

     p(auxsem);
     putZeros (cpshm, MAX_MSG_SIZE);
     v(auxsem);

    shm->pshm = (char *) pshm;
     shm->sem = auxsem;

    return shm;
}

/* Envia Informacion */
void
send_data_shm (shmADT conn, char * s_msg, int size)
{

    while (p(conn->sem))
    {
        if (conn->pshm[0] != 0)
        {
            v(conn->sem);
            usleep (500000);
        }
        else
            break;
    }
    /* Para indicar que hay un mensaje nuevo */
    *(conn->pshm) = 1;
    strcpy (conn->pshm + 1, s_msg);
    v(conn->sem);

    return;
}

/* Recibe Informacion */
void
rcv_data_shm (shmADT conn, char * r_msg)
{
    p (conn->sem);
    strcpy (r_msg, conn->pshm+1);
    putZeros (conn->pshm, MAX_MSG_SIZE);
    v(conn->sem);

    return;
}

/* Cerrar Conexion */
int
close_conn_shm (shmADT conn)
{
    p (conn->sem);
    /* Para indicar que me desconecto */
    *(conn->pshm + 1) = 3;
    v(conn->sem);
    shmdt (conn->pshm);
    free (conn);

    return 1;
}

void
putZeros (char * arr, int len)
{
    int i;
    for (i=0; i<len; i++)
        arr[i] = 0;

    return ;
}

int
walkDirSM(char *path, char *relPath, shmADT snd, shmADT rcv)
{
    DIR *dip;
    struct dirent *dit;
    int i = 0;
    char *ext;
    char currPath[FILE_LEN];
    char currRelPath[FILE_LEN];

    /*
    * Open a directory stream to argv[1] and make sure
        * it's a readable and valid (directory) */
    if ((dip = opendir(path)) == NULL)
    {
        perror("opendir");
        return 0;
    }

    /*
    * Read in the files from argv[1] and print */
    while ((dit = readdir(dip)) != NULL)
    {
        i++;
        sprintf(currPath, "%s/%s", path, dit->d_name);

        /*TODO: Si se le pasa un path roto es probable que muera,
        no se pone ahora porque toda la info sale de readdir
        No envia archivos ni dirs que empiecen con '.'*/
        if (isDir(currPath))
        {
            if ( 0 == strcmp(".", dit->d_name) ||
                 0 == strcmp("..", dit->d_name) )
                continue;


            sprintf(currRelPath, "%s/%s", relPath, dit->d_name);

            if(send_file_shm(snd, rcv, currPath, 1, 1) < 0)
            {
//                 TODO aca esta dando mal
                printf("si currPath es dir, currPath: %s\n", currPath);
                fatal("Error sending file\n");
            }
            
            walkDirSM(currPath, currRelPath, snd, rcv);


        }
        else
        {
            /* Don't send .mine's */
            ext = fileExt(dit->d_name);
            if (    0 == strcmp(".mine", ext) ||
                    0 == strcmp(".new", ext) )
                continue;

            if(send_file_shm(snd, rcv, currPath, 0, 1) < 0)
            {
                //TODO aca esta dando mal
                printf("si currPath No es dir, currPath: %s\n", currPath);
                fatal("Error sending file\n");
            }
        }
    }

	    /*
    * Close the stream. And check for errors. */
    if (closedir(dip) == -1)
    {
        perror("closedir");
        return 0;
    }

    return 1;
}

int send_file_shm (shmADT snd, shmADT rcv, char *fname, int isdir, int who)
{
    unsigned int fsize;
    int back, tot_len, name_len, acum, cant, nread;
    FILE * pfile;
    char buf[BUF_SIZE] = {0};
    char aux_buf[BUF_SIZE+1] = {0};
    char msg[MAX_MSG_SIZE] = {0};
    char answ[MAX_MSG_SIZE] = {0};
    int me;

    if(isDir(fname) == 1)
    {
        sprintf(msg, "%d,%s", -1, fname);
        name_len = strlen(msg);
        sprintf(answ, "%d,%d,%d,%s", who, SND_FILE, name_len, msg);

        tot_len = strlen(answ);
        send_data_shm(snd, answ, tot_len);


        if (who == 1)
        {
            while(!msgForServer(rcv))
            ;
        }
        else if (who == 0)
        {
            while(!msgForClient(rcv))
                ;
        }
        else
            printf("Error in send_file_shm\n");
            
        clrtext(answ, MAX_MSG_SIZE); 

        rcv_data_shm(rcv, answ);
        sscanf(answ, "%d,%d", &me, &back);

//             TODO debug
        printf("msg recibido: %s\n", answ);

        if(back != COM_ACK)
        {
            fatal("Error received\n");
            return -1;
        }

        return 1;
    }

    fsize = getFileSize(fname);

    if ((pfile = fopen(fname, "r")) == NULL)
        fatal ("file open failed\n");

    sprintf(msg, "%d,%s", fsize, fname);
    name_len = strlen(msg);
    sprintf(answ, "%d,%d,%d,%s", who, SND_FILE, name_len, msg);

    tot_len = strlen(answ);

    send_data_shm(snd, answ, tot_len);

    if (who == 1)
    {
        while(!msgForServer(rcv))
            ;
    }
    else if (who == 0)
    {
        while(!msgForClient(rcv))
            ;
    }
    else
        printf("Error in send_file_shm\n");

    clrtext(answ, MAX_MSG_SIZE);
    rcv_data_shm(rcv, answ);

// 	TODO
    printf("answ: %s\n", answ);

    sscanf(answ, "%d,%d", &me, &back);
    if(back != COM_ACK)
    {
        fatal("Error received\n");
        return -1;
    }

    acum = 0;
    while(acum < fsize)
    {
        if((fsize - acum) > BUF_SIZE)
                cant = BUF_SIZE;
        else
                cant = (fsize - acum);

        nread = fread(buf, 1, cant, pfile);
        if (nread < BUF_SIZE && nread < (fsize - acum))
        {
                fatal ("File read failure\n");
        }

        acum += nread;

       // clrtext(aux_buf, BUF_SIZE+1);
        sprintf(aux_buf, "%d%s", who, buf);
        do{
            printf("UNO\n");
            send_data_shm(snd, aux_buf, nread);
            printf("DOS\n");
            if (who == 1)
            {
                while(!msgForServer(rcv))
                    ;
            }
            else if (who == 0)
            {
                while(!msgForClient(rcv))
                    ;
            }
            else
                printf("Error in send_file_shm\n");

            printf("TRES\n");
            clrtext(answ, MAX_MSG_SIZE);
            rcv_data_shm(rcv, answ);
            sscanf(answ, "%d,%d", &me, &back);
            printf("ACA: %d\n", back);

        }while(back != COM_ACK);
    }

    return 1;
}


int msgForServer (shmADT s)
{
    char * shm;

    shm = getShm(s);
    if(shm[1] == '0')
        return 1;

    usleep(100);
    return 0;
}

int msgForClient (shmADT s)
{
    char * shm;

    shm = getShm(s);
    if(shm[1] == '1')
        return 1;

    usleep(100);

    return 0;
}

int getNum_shm (char * msg, int * ret)
{
    int i = 0, j = 0;
    char aux[FILE_LEN] = {0};
    if (msg != NULL)
    {
        while (*msg == ',')
        {
            msg++;
            j++;
        }
        while(msg[i] != ',' && msg[i] != 0)
        {
            aux[i] = msg[i];
            i++;
        }

        if (msg[i] != 0)
            i++;

        sscanf(aux,"%d", ret);
        return i + j;
    }
    return -1;
}
